import { ActionEnum } from './shared';
import { pickBy, isNull, cloneDeep } from 'lodash';
import { TablePreviewProps } from './interfaces';
import { MutableRefObject } from 'react';

declare type mapKey = string | number;

interface pageData {
  total: number;
  data: Array<any>;
}

interface paginationData {
  pageSize: number;
  current: number;
}

interface changePageData {
  (param: pageData, param2?: paginationData): pageData;
}

enum OptionGroup {
  add = 'add',
  edit = 'edit',
  del = 'del',
}

type DataMapString = 'cacheAdd' | 'cacheDelete' | 'cacheEdit';

export declare type CacheType =
  | 'changeCacheAdd'
  | 'changeCacheEdit'
  | 'changeCacheDelete'
  | undefined;

const notNull = (data: any, key: string) =>
  (['$$pid', '_create', 'id', '$$child', 'inBizTableIndex'].includes(key) && !isNull(data)) ||
  !['$$pid', '_create', 'id', '$$child', 'inBizTableIndex'].includes(key);

const random = (): string => {
  const s = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890';
  let l = s.length;
  let n = '';
  for (let i = 1; i < 30; i++) {
    if (i % 6 || !i) {
      n += s.charAt(Math.floor(Math.random() * l));
    } else {
      n += '-';
    }
  }
  return '$$create-' + n;
};

const BOND_STR = '__bonding__';

class DataSetCache {
  public parentModel: string | undefined;
  public clickRowData: any;
  public primaryKey: string = 'id';
  public cacheParent: string | undefined; //缓存当前页面的上级唯一键值
  public cacheDelete: Map<mapKey, any> = new Map();
  public cacheAdd: Map<mapKey, any> = new Map();
  public cacheAddKeyList: Array<string> = [];
  public cacheEdit: Map<mapKey, any> = new Map();
  public cachePageSize: number | undefined;
  public cacheType: CacheType;
  public pageTotal: number = -1;
  public editData: any = {};
  public queryArea: [number, number] = [0, 0];
  public valueCode = 'masterDatas';
  public cacheChangeKey: Array<string> = []; //记录编辑状态下修改过的属性，不然后台无法做批量更新
  public pageData: Array<any> = []; //缓存页面数据
  readonly eventsRef: MutableRefObject<Exclude<TablePreviewProps["callBackEvents"], undefined>>
  constructor(eventsRef: DataSetCache["eventsRef"]) {
    this.eventsRef = eventsRef;
  }
  /**
   * 更新缓存数据， 和loadData配套使用
   */
  updateData = ({list, oldList, delChildrenAdd, changeParentStatus, isDesc}:{
    list: Record<string, unknown>[], 
    oldList: Record<string, unknown>[],
    delChildrenAdd:Function,
    changeParentStatus: Function,
    isDesc?: boolean
  }) => {
    const editKeys:string[] = [];
    list.forEach(item => {
      if (isDesc) {
        Object.keys(item).forEach(key => {
          if (!key.includes('@desc') && item[key + '@desc'] === undefined) {
            item[key + '@desc'] = item[key]; 
          }
        });
        delete item['masterDatas@desc'];
      }
      if (item?.masterDatas === undefined) {
        item.masterDatas = {...item}
      }
      if (item[this.primaryKey] === undefined) {
        // 新增
        this.cacheStatus(ActionEnum.add, item);
      } else {
        editKeys.push(item[this.primaryKey] as string);
        // 修改
        this.cacheStatus(ActionEnum.edit, item);
      }
      changeParentStatus();
      this.changeCache(item, true);
      if (item[this.primaryKey] === undefined) {
        item[this.primaryKey] = this.editData[this.primaryKey];
      }
    });
    // 没有在新列表中的数据就都走删除
    oldList.filter(item => !editKeys.includes(item[this.primaryKey] as string)).forEach(item => {
      this.cacheStatus(ActionEnum.del, item);
      delChildrenAdd(item);
      this.changeCacheDelete();
    })
    this.pageData = list;
    return list
  }
  /**
   * 组装id
   */
  public formatId = (id?: string) => {
    if (`${id ?? ''}`.indexOf(BOND_STR) >= 0) {
      return id;
    }
    return this.cacheParent + BOND_STR + id ?? this.editData.id;
  };

  /**
   * 还原id
   */
  public resetId = (id: string) => {
    const idArray: Array<string> =
      `${id}`.indexOf(BOND_STR) < 0 ? ['', id] : id?.split(BOND_STR).slice(-2);
    return idArray;
  };

  /**
   * 判断数据是否为新增
   */
  public isNewCreate = () => {
    return this.cacheAdd.has(this.formatId(this.editData[this.primaryKey])!);
  };

  /**
   * 缓存当前操作类型与状态
   */
  public cacheStatus = (type: ActionEnum, data: any) => {
    const typeMap = {
      [ActionEnum.add]: 'changeCacheAdd',
      [ActionEnum.edit]: 'changeCacheEdit',
      [ActionEnum.del]: 'changeCacheDelete',
    };
    this.cacheType = typeMap[type];
    this.editData = data
      ? {
        ...data,
        [this.primaryKey]: this.formatId(data[this.primaryKey]),
      }
      : { _create: true };
  };

  /**
   * 修改缓存
   */
  public changeCache = async (data: any, isLoad?: boolean) => {
    if (this.cacheType) {
      return this[this.cacheType](data, isLoad);
    }
    return;
  };

  /**
   * 修改分页数据
   */
  public changePage = (type: string, page: pageData) => {
    const typeMap = {
      [ActionEnum.add]: 'changeOriginAdd',
      [ActionEnum.edit]: 'changeOriginEdit',
      [ActionEnum.del]: 'changeOriginDelete',
    };
    const actionFunc = typeMap[type];
    return this[actionFunc]({
      ...page,
      total: (isNaN(page.total) ? page?.data?.length : page.total) || 0
    });
  };

  /**
   * 组装数据
   * 当数据修改时，只缓存修改后的值与最初值不一样的属性
   */
  public assembleData = (data: any) => {
    let type = 'cacheEdit';
    if (this.isNewCreate()) {
      type = 'cacheAdd';
    }
    const beforeValue = this[type].get(this.editData[this.primaryKey]);
    // const beforeRelationDatas=beforeValue.relationDatas??[]
    const beforemasterDatas = beforeValue.masterDatas ?? {};
    const nextmasterDatas = { ...beforemasterDatas, ...(data.masterDatas ?? {}) };
    // const nextRelationDatas=
    this[type].set(this.editData[this.primaryKey], {
      ...this.editData,
      ...nextmasterDatas,
      masterDatas: nextmasterDatas,
    });
  };

  /**
   * 修改新增缓存
   */
  public changeCacheAdd = (data: any, isLoad?:boolean) => {
    const allKeys = Object.keys(data.masterDatas ?? {});
    const _nextmasterDatas = Object.entries(data.masterDatas ?? {}).reduce((prev, [key, value]) => {
      if (key.indexOf('@desc') >= 0) {
        return prev;
      } else if (
        !isLoad && allKeys.includes(`${key}@desc`) &&
        key.indexOf('$raw') < 0 &&
        (data.masterDatas ?? {})[`${key}@desc`]
      ) {
        const middleValue = cloneDeep(value);
        prev[key] = (data.masterDatas ?? {})[`${key}@desc`];
        prev[`${key}@desc`] = middleValue;
        return prev;
      }
      return {
        ...prev,
        [key]: value,
      };
    }, {});

    const _id = random();
    this.cacheAdd.set(this.formatId(_id)!, {
      _create: true,
      ...data.masterDatas,
      masterDatas: _nextmasterDatas,
      [this.primaryKey]: this.formatId(_id),
    });
    this.cacheAddKeyList.push(this.formatId(_id)!);
    this.editData = {
      ...data.masterDatas,
      masterDatas: _nextmasterDatas,
      [this.primaryKey]: this.formatId(_id),
      _create: true,
    };
    return ActionEnum.add;
  };

  /**
   * 数据新增后，重新整理当前页面数据
   */
  public changeOriginAdd: changePageData = ({ data, total }) => {
    const nextData = [...(data || []), this.editData];

    return {
      data: nextData,
      total: total + 1,
    };
  };

  /**
   * 修改编辑缓存
   * 1.如果是新增的数据则修改新增缓存
   * 2.如果是已有数据，则修改编辑缓存
   */
  public changeCacheEdit = (data: any, isLoad?: boolean) => {
    let type = 'cacheEdit';
    if (this.isNewCreate()) {
      type = 'cacheAdd';
    }
    const beforeValue = this[type].get(this.editData[this.primaryKey]);
    const beforemasterDatas = beforeValue?.masterDatas ?? {};
    const allKeys = Object.keys(data.masterDatas ?? {});
    const _nextmasterDatas = Object.entries(data.masterDatas ?? {}).reduce((prev, [key, value]) => {
      if (key.indexOf('@desc') >= 0) {
        return prev;
      } else if (
        !isLoad && allKeys.includes(`${key}@desc`) &&
        key.indexOf('$raw') < 0 &&
        (data.masterDatas ?? {})[`${key}@desc`]
      ) {
        const middleValue = cloneDeep(value);
        prev[key] = (data.masterDatas ?? {})[`${key}@desc`];
        prev[`${key}@desc`] = middleValue;
        return prev;
      }
      return {
        ...prev,
        [key]: value,
      };
    }, {});
    const nextmasterDatas = { ...beforemasterDatas, ..._nextmasterDatas };

    // 修复行内编辑删除后，原生数据没有删除
    Object.keys(nextmasterDatas).forEach(key => {
      if (!key.includes('@desc') && !nextmasterDatas[key] && nextmasterDatas[key] !== 0 && nextmasterDatas[key + '@desc']) {
        nextmasterDatas[key + '@desc'] = undefined;
      }
      if (key.includes('@desc') && nextmasterDatas[key] && nextmasterDatas[key.replace('@desc', '')] === undefined) {
        nextmasterDatas[key] = undefined
      }
    })

    if (!this.isNewCreate()) {
      const keys = Object.keys(_nextmasterDatas);
      this.cacheChangeKey = [...new Set<string>([...this.cacheChangeKey, ...keys])];
    }
    //处理格式化
    const keysMap = Object.keys(nextmasterDatas).reduce((prev, next) => {
      prev[`${next}$raw`] = null;
      return prev;
    }, {});
    // const nextRelationDatas=
    this[type].set(this.editData[this.primaryKey], {
      ...this.editData,
      ...nextmasterDatas,
      ...keysMap,
      masterDatas: nextmasterDatas,
      id: this.formatId(this.editData[this.primaryKey]),
    });
    return ActionEnum.edit;
  };

  /**
   * 编辑完数据后，重新整理当前页面数据
   */
  public changeOriginEdit: changePageData = ({ data, total }) => {
    const nextList = data.map((ele, index) => {
      let currentId = this.formatId(ele[this.primaryKey]);
      let targetId = this.formatId(this.editData[this.primaryKey]);
      let type = 'cacheEdit';
      if (this.isNewCreate()) {
        type = 'cacheAdd';
      }
      if (currentId === targetId) {
        const currentData = {
          ...this[type].get(currentId),
          [this.primaryKey]: type === 'cacheAdd' ? targetId : this.resetId(targetId!)[1],
        };
        return this.eventsRef.current.onEditRow?.(currentData, index) ?? currentData
      }
      return ele;
    });
    this.pageData = nextList;
    return {
      data: nextList,
      total: total || nextList.length,
    };
  };

  /**
   * 修改删除缓存
   * 1.如果是新增的数据则直接删除
   * 2.如果是后台已有数据，则缓存到cacheDelete里面
   */
  public changeCacheDelete = () => {
    if (this.isNewCreate()) {
      this.cacheAdd.delete(this.formatId(this.editData[this.primaryKey])!);
      this.cacheAddKeyList = this.cacheAddKeyList.filter(
        (ele) => ele !== this.formatId(this.editData[this.primaryKey]),
      );
    } else {
      this.cacheDelete.set(this.formatId(this.editData[this.primaryKey])!, this.editData);
    }
    return ActionEnum.del;
  };

  /**
   * 删除完数据后，重新整理当前页面数据
   */
  public changeOriginDelete: changePageData = ({ data, total }) => {
    return {
      data: data.filter(
        (ele) =>
          this.resetId(ele[this.primaryKey])[1] !== this.resetId(this.editData[this.primaryKey])[1],
      ),
      total: total - 1,
    };
  };

  /**
   * 获取到单条缓存数据
   * @param data 查询数据
   */
  public getCacheData = (data: any) => {
    //TODO 判断是否是编辑
    let targetId = data && data[this.primaryKey];
    if (data && `${data[this.primaryKey] ?? ''}`.indexOf(BOND_STR) < 0) {
      targetId = this.formatId(data[this.primaryKey]);
    }
    // const targetId=data && this.formatId(data.id)
    if (data && this.cacheEdit.has(targetId)) {
      return {
        ...this.cacheEdit.get(targetId),
        id: data[this.primaryKey],
      };
    } else if (data && this.cacheAdd.has(targetId)) {
      return {
        ...this.cacheAdd.get(targetId),
        id: data[this.primaryKey],
      };
    }
    return {};
  };

  /**
   * 将请求到的原始数据与缓存数据比较,
   * 如果有相同id的则应用缓存数据，没有则立即返回
   */
  public getQueryMapData = (originData: Array<any>) => {
    const addList = [...this.cacheAdd.values()];
    const filterDelteData = originData.filter(
      (ele) => !this.cacheDelete.has(this.formatId(ele[this.primaryKey])!),
    );
    const leftData = filterDelteData.map((ele) => {
      if (this.cacheEdit.has(this.formatId(ele[this.primaryKey])!)) {
        return {
          ...this.cacheEdit.get(this.formatId(ele[this.primaryKey])!),
          id: ele[this.primaryKey],
        };
      }
      return ele;
    });
    const currentAddData = addList.filter((ele) => {
      const idList = this.resetId(ele[this.primaryKey]);
      return idList[0] === this.cacheParent;
    });
    return [...leftData, ...currentAddData];
  };

  /**
   * 初始化原始数据条数
   * @param total 总页数
   */
  public initPageTotal = (total: number) => {
    if (this.pageTotal === -1) {
      this.pageTotal = total;
    }
  };

  /**
   * 获取到对应范围内的新增数据
   * @returns Array<any>
   */
  public getRangeArray = () => {
    const result = [];
    const array = this.cacheAddKeyList;
    const [start, end] = this.queryArea;
    let current = start;
    while (current <= end && current < array.length) {
      const key = array[current];
      const nextData = this.cacheAdd.get(key);
      result.push(nextData);
      current++;
    }
    return result;
  };

  /**
   * 判断是否需要请求后台
   * @param newParam 查询参数
   * @returns
   */
  public queryAync = async (newParam: any) => {
    const {
      pageInfo: { index, size },
    } = newParam;
    const startSize = size * (index - 1);
    const nextSize = size * index;
    if (startSize >= this.pageTotal) {
      const diff = size * (index - 1) - this.pageTotal;
      this.queryArea = [diff, diff + size];
      return undefined;
    } else {
      const hasCache = startSize <= this.pageTotal && this.pageTotal <= nextSize;
      const diff = hasCache ? nextSize - this.pageTotal : -1;
      this.queryArea = [0, diff];
      return newParam;
    }
  };

  /**
   * 加载父级为子表格且选中数据为新增时的子数据
   */
  public getAllParentNewData = () => {
    const addList = [...this['cacheAdd'].values()];
    const targetList = addList.filter((ele: any) => {
      return (
        ele[this.primaryKey].indexOf(this.cacheParent) >= 0 &&
        ele[this.primaryKey].length > (this.cacheParent?.length ?? 0)
      );
    });
    return targetList;
  };

  /**
   * 子集表格选中数据后反查父级是否含有
   * 没有则修改状态，默认为edit
   */
  public childSetParentData = (currentId: string) => {
    let idFromChild = `${currentId}`;
    // if (idFromChild.indexOf(BOND_STR) >= 0) {
    idFromChild = this.resetId(currentId)[1];
    // }
    const dataList = this.throwChangeValue();
    const donotHave = !dataList.some((child: any) => {
      let innerId = child[this.primaryKey] ?? child.$$child;
      return innerId === idFromChild;
    });
    if (donotHave) {
      this.cacheEdit.set(this.formatId(idFromChild), {
        id: this.formatId(idFromChild),
        $$child: currentId,
        _opt: 'edit',
      });
    }
  };

  /**
   * 父表格删除新增数据，删除对应子表格里面的数据
   */
  public parentDelChild = (parenId: string) => {
    const leftKeyList = this.cacheAddKeyList.filter((ele) => {
      const current = this.resetId(ele)[0];
      return current !== parenId;
    });
    const keys = [...this.cacheAdd.keys()];
    keys.forEach((ele) => {
      if (!leftKeyList.includes(ele as string)) {
        this.cacheAdd.delete(ele);
      }
    });
  };

  public fillData = (ele, type) => {
    if (type !== OptionGroup.edit) {
      return {};
    }
    const index = this.pageData.findIndex((child) => {
      const _id = ele[this.primaryKey].split(BOND_STR).slice(-1)[0];
      return child[this.primaryKey] === _id;
    });
    const data = this.pageData[index];
    const result = {};
    for (let i = 0; i < this.cacheChangeKey.length; i++) {
      result[this.cacheChangeKey[i]] = data[this.cacheChangeKey[i]];
    }
    return result;
  };

  /**
   * 格式化数据, 是否需要@desc转换成value
   * isOrigin 为true时，不获取@desc数据，直接获取源数据
   */
  public tranformValue = (data, isOrigin: boolean) => {
    if (!data) {
      return;
    }
    const allKeys = Object.keys(data);
    const result = Object.entries(data).reduce((prev, [key, value]) => {
      if (key.indexOf('@desc') >= 0) {
        return prev;
      }
      if (allKeys.includes(`${key}@desc`) && key.indexOf('$raw') < 0) {
        prev[key] = isOrigin ? ( data[key] || data[`${key}@desc`]) : (data[`${key}@desc`] || data[key]);
        return prev;
      }
      return {
        ...prev,
        [key]: value,
      };
    }, {});
    return result;
  };

  /**
   * 将修改的数据抛出到ref上
   */
  public throwChangeValue = () => {
    const iterableToArray = (attr: DataMapString, type: OptionGroup) => {
      return [...this[attr].values()].map((ele) => {
        const idList = this.resetId(ele[this.primaryKey]);

        let result = ele.masterDatas
          ? {
            ...this.fillData(ele, type),
            ...this.tranformValue(ele.masterDatas),
            $$pid: idList[0],
            $$child: idList[1],
            id: ele._create ? null : idList[1],
          }
          : {
            ...this.fillData(ele, type),
            ...this.tranformValue(ele),
            _create: null,
            $$pid: idList[0],
            $$child: idList[1],
            id: ele._create ? null : idList[1],
          };
        if (type === OptionGroup.del) {
          result = { id: idList[1], $$pid: idList[0] };
        }

        return pickBy(
          {
            ...result,
            _opt: type,
          },
          notNull,
        );
      });
    };
    const result = [
      ...iterableToArray('cacheAdd', OptionGroup.add),
      ...iterableToArray('cacheEdit', OptionGroup.edit),
      ...iterableToArray('cacheDelete', OptionGroup.del),
    ].map((ele) => ({
      ...ele,
    }));

    this.cacheChangeKey = [];
    return result;
  };

  /**
   * 重置状态
   */
  reset = () => {
    this.cacheDelete = new Map();
    this.cacheAdd = new Map();
    this.cacheEdit = new Map();
    this.cacheAddKeyList = [];
    this.editData = {};
    this.cacheParent = undefined;
  };
}

export default DataSetCache;
